import { PluginContext, TransformHook } from 'rollup'
import { ResolvedOptions } from './options'

export type LoaderFunction = (
  /** @deprecated */
  this: PluginContext,
  files: string[],
  config: BaseResolvedGlobConfig
) => string | Iterable<string> | Promise<string | Iterable<string>>

export type Loader =
  | LoaderFunction
  | {
      loadByImporter?: (
        importer: string,
        config: BaseResolvedGlobConfig
      ) => string | Iterable<string> | Promise<string | Iterable<string>>
      load: LoaderFunction
      transform?: TransformHook
      invalidateOnChanged?: boolean
    }

interface BaseGlobConfig {
  /** Provide the virtual ID of the imported module. */
  aliases?: string | string[]
  /** Glob expression to import. */
  glob: string
  exclude?: string | string[]
  transform?: TransformHook
}

export interface CustomGlobConfig extends BaseGlobConfig {
  loader?: Loader
  [x: string]: any
}

interface GlobConfig__import extends BaseGlobConfig {
  loader: 'import'
  dynamicImport?: boolean | ((filename: string) => boolean)
}

interface GlobConfig__default extends BaseGlobConfig {
  loader: 'default'
  dynamicImport?: boolean | 'async' | ((filename: string) => boolean | 'async')
  moduleKeyRenamer?: (filename: string, config: BaseResolvedGlobConfig) => string
  moduleIdRenamer?: (filename: string, config: BaseResolvedGlobConfig) => string
}

interface GlobConfig__named extends BaseGlobConfig {
  loader: 'named'
}

interface GlobConfig__namedModule extends BaseGlobConfig {
  loader: 'namedModule'
  dynamicImport?: boolean | 'async' | ((filename: string) => boolean | 'async')
  moduleKeyRenamer?: (filename: string, config: BaseResolvedGlobConfig) => string
  moduleIdRenamer?: (filename: string, config: BaseResolvedGlobConfig) => string
}

interface GlobConfig__mixed extends BaseGlobConfig {
  loader: 'mixed'
  moduleIdRenamer?: (filename: string, config: BaseResolvedGlobConfig) => string
  moduleNamedExportRenamer?: (
    filename: string,
    name: string,
    config: BaseResolvedGlobConfig
  ) => string
}

export type VueRouteRaw = Omit<
  // @ts-ignore
  import('vue-router').RouteRecordRaw,
  'component' | 'components' | 'children' | 'name'
> & { component?: string; children?: VueRouteRaw[]; name: string; [x: keyof any]: any }

interface GlobConfig__vue_routes extends BaseGlobConfig {
  loader: 'vue-routes'
  base?: string
  nuxtStyle?: boolean
  importMode?:
    | 'sync'
    | 'async'
    | 'auto'
    | ((route: VueRouteRaw, parent: VueRouteRaw | undefined) => any)
  routeName?: (filename: string, config: BaseResolvedGlobConfig) => string
  extendRoute?: (
    route: VueRouteRaw,
    parent: VueRouteRaw | undefined
  ) => VueRouteRaw | void | Promise<VueRouteRaw | void>
  onRoutesGenerated?: (
    routes: VueRouteRaw[]
  ) => VueRouteRaw[] | void | Promise<VueRouteRaw[] | void>
}

export type ReactRouteRaw = {
  caseSensitive?: boolean
  children?: ReactRouteRaw[]
  element?: string
  index?: boolean
  path: string
  [x: keyof any]: any
}

interface GlobConfig__react_routes extends BaseGlobConfig {
  loader: 'react-routes'
  base?: string
  nuxtStyle?: boolean
  importMode?:
    | 'sync'
    | 'async'
    | 'auto'
    | ((route: ReactRouteRaw, parent: ReactRouteRaw | undefined) => any)
  extendRoute?: (
    route: ReactRouteRaw,
    parent: ReactRouteRaw | undefined
  ) => ReactRouteRaw | void | Promise<ReactRouteRaw | void>
  onRoutesGenerated?: (
    routes: ReactRouteRaw[]
  ) => ReactRouteRaw[] | void | Promise<ReactRouteRaw[] | void>
}

export type GlobConfig =
  | CustomGlobConfig
  | GlobConfig__import
  | GlobConfig__default
  | GlobConfig__named
  | GlobConfig__namedModule
  | GlobConfig__mixed
  | GlobConfig__vue_routes
  | GlobConfig__react_routes

export interface BaseResolvedGlobConfig {
  id: string
  aliases: string[]
  glob: string
  globState: import('picomatch/lib/scan').State
  globRegExp: RegExp
  exclude: string[]
  loadByImporter?: (
    this: PluginContext,
    importer: string,
    config: BaseResolvedGlobConfig
  ) => string | Iterable<string> | Promise<string | Iterable<string>>
  load: LoaderFunction
  query?: string
  hash?: string
  invalidateOnChanged?: boolean
  transform?: TransformHook
  resolvedOptions: ResolvedOptions
  start(context: PluginContext): Promise<void>
  generateCode(context?: PluginContext): Promise<string>
  close(): void
  [x: string]: any
}

export interface UserOptions {
  globConfigs?: GlobConfig | GlobConfig[]
  defaultLoader?:
    | 'import'
    | 'default'
    | 'named'
    | 'namedModule'
    | 'mixed'
    | 'vue-routes'
    | 'react-routes'
    | Loader
  exclude?: string | string[]
  extensions?: string[]
}
